home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 January / PCWorld_2007-01_cd.bin / v cisle / hotkey / AutoHotkey104504_Install.exe / Extras / ahk-mode.el < prev    next >
Lisp/Scheme  |  2005-06-07  |  19KB  |  568 lines

  1. ;;; ahk-mode.el --- major mode for editing AutoHotKey scripts for X/GNU Emacs
  2.  
  3. ;; Copyright (C) 2005 Robert Widhopf-Fenk
  4.  
  5. ;; Author:   Robert Widhopf-Fenk
  6. ;; Keywords: AutoHotKey, major mode
  7. ;; X-URL:    http://www.robf.de/Hacking/elisp
  8. ;; arch-tag: 1ae180cb-002e-4656-bd9e-a209acd4a3d4
  9. ;; Version:  $Id: ahk-mode--main--1.0--patch-4$
  10.  
  11. ;; This code is free software; you can redistribute it and/or modify
  12. ;; it under the terms of the GNU General Public License as published by
  13. ;; the Free Software Foundation; either version 2, or (at your option)
  14. ;; any later version.
  15. ;;
  16. ;; This program is distributed in the hope that it will be useful,
  17. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. ;; GNU General Public License for more details.
  20. ;;
  21.  
  22. ;;; Commentary:
  23. ;;
  24. ;; AutoHotKey: Automation, Hotkeys and Scripting for Windows at
  25. ;; http://www.autohotkey.com/ is a cool tool to make daily life
  26. ;; with Windows easier or even fun!
  27. ;;
  28. ;; This is a X/GNU Emacs mode for editing AutoHotKey scripts.
  29. ;;
  30. ;; Place this file somewhere in your load-path, byte-compile it and add the
  31. ;; following line to your ~/.xemacs/init.el resp. ~/.emacs:
  32. ;;
  33. ;; (setq ahk-syntax-directory "PATHTO/AutoHotkey/Extras/Editors/Syntax/")
  34. ;; (add-to-list 'auto-mode-alist '("\\.ahk$" . ahk-mode))
  35. ;; (autoload 'ahk-mode "ahk-mode")
  36. ;; 
  37. ;; The first time ahk-mode.el is started it will ask you for the path to the
  38. ;; Syntax directory if not set already.  You will find it as a subdirectory
  39. ;; of your AHK installation.
  40. ;;
  41. ;; For example if you installed AHK at C:\Programms\AutoHotKey it will be
  42. ;; C:/Programms/AutoHotKey/Extras/Editors/Syntax or a corresponding cygwin
  43. ;; path!
  44. ;;
  45. ;; When opening a script file you will get:
  46. ;; - syntax highlighting
  47. ;; - indention, completion and command help (bound to "TAB")
  48. ;; - insertion of command templates (bound to "C-c C-i") 
  49. ;; - electric braces (typing "{" will also insert "}" and place point in
  50. ;;   between) 
  51. ;; - lookup the docs on a command via w3 (place point on command and type
  52. ;;   "C-c C-h")
  53. ;;
  54. ;; Please send bug-reports or feature suggestions to hackATrobfDOTde.
  55.  
  56. ;;; Bugs:
  57. ;;
  58. ;; - completions is not really context aware
  59. ;; - multi-line comments are not fontified correctly while editing,
  60. ;;   but only when fontifying the whole buffer.  If you know how to
  61. ;;   fix this, please let me know!
  62.  
  63. ;;; History:
  64. ;;
  65. ;; The CHANGELOG is stored in my arch repository.
  66. ;;
  67. ;; If you wonder what arch is, take a look at http://wiki.gnuarch.org/ !
  68.  
  69. (eval-when-compile
  70.   (require 'font-lock)
  71.   (if (locate-library "w3") (require 'w3))
  72.   (require 'cl))
  73.  
  74. ;;; Code:
  75. (defgroup ahk-mode nil
  76.   "A mode for AutoHotKey"
  77.   :group 'languages
  78.   :prefix "ahk-")
  79.  
  80. (defcustom ahk-mode-hook '(ahk-mode-hook-activate-filling)
  81.   "Hook functions run by `ahk-mode'."
  82.   :type 'hook
  83.   :group 'ahk-mode)
  84.  
  85. (defcustom ahk-indetion 2
  86.   "The indetion level."
  87.   :type 'integer
  88.   :group 'ahk-mode)
  89.  
  90. (defcustom ahk-syntax-directory nil
  91.   "The indetion level."
  92.   :type 'directory
  93.   :group 'ahk-mode)
  94.  
  95. ;;;###autoload
  96. (add-to-list 'auto-mode-alist
  97.              '("\\.ahk$"  . ahk-mode))
  98.  
  99. (defvar ahk-mode-syntax-table
  100.   (let ((table (make-syntax-table)))
  101.     ;; these are also allowed in variable names
  102.     (modify-syntax-entry ?#  "w" table)
  103.     (modify-syntax-entry ?_  "w" table)
  104.     (modify-syntax-entry ?@  "w" table)
  105.     (modify-syntax-entry ?$  "w" table)
  106.     (modify-syntax-entry ??  "w" table)
  107.     (modify-syntax-entry ?[  "w" table)
  108.     (modify-syntax-entry ?]  "w" table)
  109.     ;; some additional characters used in paths and switches  
  110.     (modify-syntax-entry ?\\  "w" table)
  111. ;    (modify-syntax-entry ?/  "w" table)
  112.     (modify-syntax-entry ?-  "w" table)
  113.     (modify-syntax-entry ?:  "w" table)
  114.     (modify-syntax-entry ?.  "w" table)
  115.     ;; for multiline comments (taken from cc-mode)
  116.     (modify-syntax-entry ?/  ". 14" table)
  117.     (modify-syntax-entry ?*  ". 23"   table)
  118.     ;; Give CR the same syntax as newline, for selective-display
  119.     (modify-syntax-entry ?\^m "> b" table)
  120.     (modify-syntax-entry ?\n "> b"  table)
  121.     table)
  122.   "Syntax table used in `ahk-mode' buffers.")
  123.  
  124. (defvar ahk-mode-abbrev-table
  125.   (let ((a (make-abbrev-table)))
  126.     a)
  127.   "Abbreviation table used in `ahk-mode' buffers.")
  128.  
  129. (defvar ahk-mode-map
  130.   (let ((map (make-sparse-keymap)))
  131.     (define-key map "\C-c\C-h" 'ahk-www-help-at-point)
  132.     (define-key map "\C-c\C-c" 'ahk-comment-region)
  133.     (define-key map "\C-c\C-i" 'ahk-insert-command-template)
  134.     (define-key map "\t" 'ahk-indent-line-and-complete)
  135.     (define-key map "{" 'ahk-electric-brace)
  136.     (define-key map "}" 'ahk-electric-brace)
  137.     (define-key map "\r" 'ahk-electric-return)
  138.     map)
  139.   "Keymap used in `ahk-mode' buffers.")
  140.  
  141. (defvar ahk-Commands-list nil
  142.   "A list of ahk commands and parameters.
  143. Will be initialized by `ahk-init'")
  144.  
  145. (defvar ahk-Keys-list nil
  146.   "A list of ahk key names.
  147. Will be initialized by `ahk-init'")
  148.  
  149. (defvar ahk-Keywords-list nil
  150.   "A list of ahks keywords.
  151. Will be initialized by `ahk-init'")
  152.  
  153. (defvar ahk-Variables-list nil
  154.   "A list of ahks variables.
  155. Will be initialized by `ahk-init'")
  156.  
  157. (defvar ahk-mode-font-lock-keywords nil
  158.   "Syntax highlighting for `ahk-mode'.
  159. Will be initialized by `ahk-init'")
  160.  
  161. (defvar ahk-completion-list nil
  162.   "A list of all symbols available for completion
  163. Will be initialized by `ahk-init'")
  164.  
  165. (easy-menu-define ahk-menu ahk-mode-map "AHK Mode Commands"
  166.           '("AHK"
  167.                     ["Insert Command Template" ahk-insert-command-template]
  168.                     ["Lookup webdocs on command" ahk-www-help-at-point]
  169.                     ))
  170.  
  171. (defun ahk-init ()
  172.   "Initialize ahk-mode variables.
  173. An AHK installation provides a subdirectory \"Extras/Editors/Syntax\"
  174. containing a list of keywords, variables, commands and keys.
  175.  
  176. This directory must be specified in the variable `ahk-syntax-directory'."
  177.   (interactive)
  178.  
  179.   (message "Initializing ahk-mode variables ...")
  180.   (when (null ahk-syntax-directory)
  181.     (customize-save-variable
  182.      'ahk-syntax-directory
  183.      (read-file-name "Please give the AHK-Syntax directory: "))
  184.     (custom-save-all))
  185.  
  186.   (save-excursion
  187.     (set-buffer (get-buffer-create " *ahk-mode-temp*"))
  188.   
  189.     ;; read commands
  190.     (erase-buffer)
  191.     (insert-file-contents (expand-file-name "Commands.txt"
  192.                                             ahk-syntax-directory))
  193.     (setq ahk-Commands-list nil)
  194.     (goto-char 0)
  195.     (while (not (eobp))
  196.       (if (not (looking-at "\\([^;\r\n][^(\t\r\n, ]+\\)\\([^\r\n]*\\)"))
  197.           nil;; (error "Unknown file syntax")
  198.         (setq ahk-Commands-list (cons (list
  199.                                        (match-string 1)
  200.                                        (match-string 2))
  201.                                       ahk-Commands-list)))
  202.       (forward-line 1))
  203.     
  204.     ;; read keys
  205.     (erase-buffer)
  206.     (insert-file-contents (expand-file-name "Keys.txt"
  207.                                             ahk-syntax-directory))
  208.     (setq ahk-Keys-list nil)
  209.     (goto-char 0)
  210.     (while (not (eobp))
  211.       (if (not (looking-at "\\([^;\r\n][^\t\r\n ]+\\)"))
  212.           nil;; (error "Unknown file syntax of Keys.txt")
  213.         (setq ahk-Keys-list (cons (match-string 1) ahk-Keys-list)))
  214.       (forward-line 1))
  215.     
  216.     ;; read keywords
  217.     (erase-buffer)
  218.     (insert-file-contents (expand-file-name "Keywords.txt"
  219.                                             ahk-syntax-directory))
  220.     (setq ahk-Keywords-list nil)
  221.     (goto-char 0)
  222.     (while (not (eobp))
  223.       (if (not (looking-at "\\([^;\r\n][^\t\r\n ]+\\)"))
  224.           nil;; (error "Unknown file syntax of Keywords.txt")
  225.         (setq ahk-Keywords-list (cons (match-string 1) ahk-Keywords-list)))
  226.       (forward-line 1))
  227.     ;; read variables
  228.     (erase-buffer)
  229.     (insert-file-contents (expand-file-name "Variables.txt"
  230.                                             ahk-syntax-directory))
  231.     (setq ahk-Variables-list nil)
  232.     (goto-char 0)
  233.     (while (not (eobp))
  234.       (if (not (looking-at "\\([^;\r\n][^\t\r\n]+\\)"))
  235.           nil;; (error "Unknown file syntax of Variables.txt")
  236.         (setq ahk-Variables-list (cons (match-string 1) ahk-Variables-list)))
  237.       (forward-line 1))
  238.   
  239.     ;; built completion list
  240.     (setq ahk-completion-list
  241.           (mapcar (lambda (c) (list c))
  242.                   (append (mapcar 'car ahk-Commands-list)
  243.                           ahk-Keywords-list
  244.                           ahk-Variables-list
  245.                           ahk-Keys-list)))
  246.  
  247.     (setq ahk-mode-font-lock-keywords
  248.           (list
  249.            '("\\s-*;.*$" .
  250.              font-lock-comment-face)
  251.            '("^/\\*\\(.*\r?\n\\)*\\(\\*/\\)?" .
  252. ;           '(ahk-fontify-comment .
  253.              font-lock-comment-face)
  254.            '("^\\([^ \t\n:]+\\):" .
  255.              (1 font-lock-builtin-face))
  256.            '("[^, %\"]*%[^% ]+%" .
  257.              font-lock-variable-name-face)
  258.            ;; I get an error when using regexp-opt instead of simply
  259.            ;; concatenating the keywords and I do not understand why ;-(
  260.            ;; (warning/warning) Error caught in `font-lock-pre-idle-hook': (invalid-regexp Invalid preceding regular expression)
  261.            (cons
  262.             (concat "\\b\\("
  263.                     (mapconcat 'regexp-quote ahk-Variables-list "\\|")
  264.                     "\\)\\b")
  265.             'font-lock-variable-name-face)
  266.            (list
  267.             (concat "\\(^[ \t]*\\|::[ \t]*\\)\\("
  268.                     (mapconcat 'regexp-quote (mapcar 'car ahk-Commands-list) "\\|")
  269.                     "\\)")
  270.             2
  271.             'font-lock-function-name-face)
  272.            (cons
  273.             (concat "\\b\\("
  274.                     (mapconcat 'regexp-quote ahk-Keywords-list "\\|")
  275.                     "\\)\\b")
  276.             'font-lock-keyword-face)
  277.            (cons
  278.             (concat "\\b\\("
  279.                     (mapconcat 'regexp-quote ahk-Keys-list "\\|")
  280.                     "\\)\\b")
  281.             'font-lock-constant-face)
  282.            )))
  283.   
  284.   (message "Initializing ahk-mode variables done."))
  285.  
  286. ;; this was an attempt to get multi-line comments correctly highlighted, I
  287. ;; tried to understand how cc-mode is doing it, but I have to admit I do not
  288. ;; understand it!
  289. (defun ahk-fontify-comment (limit)
  290. ;  (setq limit (point-max))
  291.   (save-excursion
  292.     (let (start end)
  293. ;      (setq bstart (save-excursion
  294. ;                     (re-search-backward "^/\\*" (point-min) t)))
  295. ;      (setq bend (save-excursion
  296. ;                   (re-search-backward "^\\*/" (point-min) t)))
  297. ;      (if (and bstart bend (< bend bstart))
  298. ;          (goto-char bstart))
  299.       (setq start (save-excursion
  300.                     (re-search-forward "^/\\*" limit t)))
  301.       (setq end (save-excursion
  302.                   (re-search-forward "^\\*/" limit t)))
  303.       
  304.       (cond
  305.        ((and start end (< end start))
  306.         (save-excursion
  307.           (re-search-forward "^\\*/" limit t)
  308.           t))
  309.        ((and start end (< start end))
  310.         (save-excursion
  311.           (re-search-forward "^/\\*\\(.*\r?\n\\)*\\*/" limit t)
  312.           t))
  313.        ((and start (not end))
  314.         (save-excursion
  315.           (re-search-forward "^/\\*\\(.*\r?\n\\)*" limit t)
  316.           t))
  317.        ))))
  318.  
  319. (defun ahk-mode-hook-activate-filling ()
  320.   "Activates `auto-fill-mode' and `filladapt-mode'."
  321.   (auto-fill-mode 1)
  322.   (if (locate-library "filladapt")
  323.       (filladapt-mode 1)))
  324.  
  325. ;;;###autoload
  326. (defun ahk-mode ()
  327.   "Major mode for editing AutoHotKey Scripts.
  328.  
  329. The hook functions in `ahk-mode-hook' are run after mode initialization.
  330.  
  331. Key bindings:
  332. \\{ahk-mode-map}"
  333.   (interactive)
  334.   (if (null ahk-Commands-list)
  335.       (ahk-init))
  336.   (kill-all-local-variables)
  337.   (set-syntax-table ahk-mode-syntax-table)
  338.   (setq major-mode 'ahk-mode
  339.     mode-name "AHK"
  340.     local-abbrev-table ahk-mode-abbrev-table
  341.     abbrev-mode t
  342.         indent-region-function 'ahk-indent-region)
  343.   (put 'ahk-mode 'font-lock-defaults '(ahk-mode-font-lock-keywords t))
  344.   (put 'ahk-mode 'font-lock-keywords-case-fold-search t)
  345.  
  346.   (when (not (featurep 'xemacs))
  347.     (setq font-lock-defaults '(ahk-mode-font-lock-keywords))
  348.     (setq font-lock-keywords-case-fold-search t))
  349.   
  350.   (use-local-map ahk-mode-map)
  351.   (easy-menu-add ahk-menu)
  352.   (setq comment-start ";")
  353.   (font-lock-mode 1)
  354.   (force-mode-line-update)
  355.   (run-hooks 'ahk-mode-hook))
  356.  
  357. (defun ahk-indent-line ()
  358.   "Indent the current line."
  359.   (interactive)
  360.  
  361.   (let ((indent 0)
  362.         (case-fold-search t))
  363.     ;; do a backward search to determin the indention level
  364.     (save-excursion
  365.       (beginning-of-line)
  366.       (if (looking-at "^;")
  367.           (setq indent 0)
  368.         (skip-chars-backward " \t\n")
  369.         (beginning-of-line)
  370.         (while (and (looking-at "^;") (not (bobp)))
  371.           (forward-line -1))
  372.         (if (looking-at "^[^: ]+:")
  373.             (if (looking-at "^[^: ]+:\\([^:]*:\\)?[ \t]*$")
  374.                 (setq indent ahk-indetion)
  375.               (setq indent 0))
  376.           (if (looking-at "^\\([ \t]*\\)[{(]")
  377.               (setq indent (+ (length (match-string 1)) ahk-indetion))
  378.             (if (and
  379.                  (save-excursion
  380.                    (forward-line 1)
  381.                    (not (looking-at "^\\([ \t]*\\)[{(]")))
  382.                  (looking-at "^\\([ \t]*\\)\\(If\\|Else\\)"))
  383.                 (setq indent (+ (length (match-string 1)) ahk-indetion))
  384.               (if (save-excursion
  385.                     (forward-line -1)
  386.                     (looking-at "^\\([ \t]*\\)\\(If\\|Else\\)"))
  387.                   (setq indent (+ (length (match-string 1))))
  388.                 (if (looking-at "^\\([ \t]*\\)")
  389.                     (setq indent (+ (length (match-string 1)))))))))))
  390.     ;; check for special tokens
  391.     (save-excursion
  392.       (beginning-of-line)
  393.       (if (looking-at "^\\([ \t]*\\)[})]")
  394.           (setq indent (- indent ahk-indetion))
  395.         (if (or (looking-at "^[ \t]*[^,: \t\n]*:")
  396.                 (looking-at "^;;;"))
  397.             (setq indent 0))))
  398.     (let ((p (point-marker)))
  399.       (beginning-of-line)
  400.       (if (looking-at "^[ \t]+")
  401.           (replace-match ""))
  402.       (indent-to indent)
  403.       (goto-char p)
  404.       (set-marker p nil)
  405.       (if (bolp)
  406.           (goto-char (+ (point) indent))))))
  407.  
  408. (defun ahk-indent-region (start end)
  409.   "Indent lines in region START to END."
  410.   (interactive "r")
  411.   (save-excursion
  412.     (goto-char end)
  413.     (setq end (point-marker))
  414.     (goto-char start)
  415.     (while (< (point) end)
  416.       (beginning-of-line)
  417.       (ahk-indent-line)
  418.       (forward-line 1))
  419.     (ahk-indent-line)
  420.     (set-marker end nil)))
  421.   
  422. (defun ahk-complete ()
  423.   "Indent current line when at the beginning or complete current command."
  424.   (interactive)
  425.  
  426.   (if (looking-at "\\w+")
  427.       (goto-char (match-end 0)))
  428.   
  429.   (let ((end (point)))
  430.     (if (and (or (save-excursion (re-search-backward "\\<\\w+"))
  431.                  (looking-at "\\<\\w+"))
  432.              (= (match-end 0) end))
  433.         (let ((start (match-beginning 0))
  434.               (prefix (match-string 0))
  435.               (completion-ignore-case t)
  436.               completions)
  437.           (setq completions (all-completions prefix ahk-completion-list))
  438.           (if (eq completions nil)
  439.               nil;(error "Unknown command prefix <%s>!" prefix)
  440.             (if (> (length completions) 1)
  441.                 (setq completions
  442.                       (completing-read "Complete command: "
  443.                                        (mapcar (lambda (c) (list c))
  444.                                                completions)
  445.                                        nil t prefix)))
  446.             (if (stringp completions)
  447.                 ;; this is a trick to upcase "If" and other prefixes
  448.                 (let ((c (try-completion completions ahk-completion-list)))
  449.                   (if (stringp c)
  450.                       (setq completions c))))
  451.             
  452.             (delete-region start end)
  453.             (if (listp completions) (setq completions (car completions)))
  454.             (insert completions)
  455.             (let ((help (assoc completions ahk-Commands-list)))
  456.               (if help (message "%s" (mapconcat 'identity help ""))))
  457.             )))))
  458.  
  459. (defun ahk-indent-line-and-complete ()
  460.   "Combines indetion and completion."
  461.   (interactive)
  462.   (ahk-indent-line)
  463.   (ahk-complete))
  464.  
  465. (defun ahk-electric-brace (arg)
  466.   "Insert character ARG and correct line's indentation."
  467.   (interactive "p")
  468.   (if (save-excursion
  469.         (skip-chars-backward " \t")
  470.         (bolp))
  471.       nil
  472.     (ahk-indent-line)
  473.     (newline))
  474.   (self-insert-command arg)
  475.   (ahk-indent-line)
  476.   (newline)
  477.   (ahk-indent-line)
  478.  
  479.   (let ((event  last-input-event))
  480.     (setq event (if (featurep 'xemacs)
  481.             (event-to-character event)
  482.           (setq event (if (stringp event) (aref event 0) event))))
  483.  
  484.     (when (equal event ?{)
  485.       (newline)
  486.       (ahk-indent-line)
  487.       (insert ?})
  488.       (ahk-indent-line)
  489.       (forward-line -1)
  490.       (ahk-indent-line))))
  491.  
  492. (defun ahk-electric-return ()
  493.   "Insert newline and indent."
  494.   (interactive)
  495.   (ahk-indent-line)
  496.   (newline)
  497.   (ahk-indent-line))
  498.  
  499. (defun ahk-insert-command-template ()
  500.   "Insert a command template."
  501.   (interactive)
  502.   (let ((completions (mapcar (lambda (c) (list (mapconcat 'identity c "")))
  503.                              ahk-Commands-list))
  504.         (completion-ignore-case t)
  505.         (start (point))
  506.         end 
  507.         command)
  508.     (setq command (completing-read "AHK command template: " completions))
  509.     (insert command)
  510.     (ahk-indent-line)
  511.     (setq end (point-marker))
  512.     (goto-char start)
  513.     (while (re-search-forward "[`][nt]" end t)
  514.       (if (string= (match-string 0) "`n")
  515.       (replace-match "\n")
  516.     (replace-match "")))
  517.     (ahk-indent-region start end)
  518.     (goto-char (1+ start))
  519.     ;; jump to first parameter 
  520.     (re-search-forward "\\<" end nil)
  521.     (set-marker end nil)))
  522.  
  523. (defun ahk-comment-region (start end &optional arg)
  524.   "Comment or uncomment each line in the region from START to END.
  525. If no region is active use the current line."
  526.   (interactive (if (region-active-p)
  527.                    (list (region-beginning)
  528.                          (region-end)
  529.                          current-prefix-arg)
  530.                  (let (start end)
  531.                    (beginning-of-line)
  532.                    (setq start (point))
  533.                    (forward-line)
  534.                    (setq end (point))
  535.                    (list start end current-prefix-arg))))
  536.   (save-excursion
  537.     (comment-region start end arg)))
  538.  
  539. (defun ahk-www-help-at-point ()
  540.   (interactive)
  541.   (save-excursion
  542.     (re-search-backward "\\<\\w+")
  543.     (if (looking-at "\\<\\w+")
  544.         (ahk-www-help (match-string 0)))))
  545.  
  546. (defvar ahk-www-help-alist '()
  547.   "Mapping of command to regexp for commands which are not unique.")
  548.  
  549. (defun ahk-www-help (command)
  550.   "Display online help for the given command"
  551.   (interactive (list
  552.                 (completing-read "AHK command: "
  553.                                  ahk-completion-list nil t)))
  554.   (require 'w3)
  555.   (w3-fetch "http://www.autohotkey.com/docs/commands.htm")
  556.   (goto-char (point-min))
  557.   (when (re-search-forward (concat "^|" (regexp-quote command)))
  558.     (goto-char (+ (match-beginning 0) 1))
  559.     (save-excursion
  560.       (if (re-search-forward (concat "^|" (regexp-quote command))
  561.                              (point-max) t)
  562.           (message "There is more than one occurrence of %s. Stopping at first match" command)
  563.         (widget-button-press (point))))))
  564.  
  565. (provide 'ahk-mode)
  566.  
  567. ;;; ahk-mode.el ends here
  568.